// Copyright 2018 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT

#include <ctype.h>
#include <inttypes.h>
#include <lib/crashlog.h>
#include <lib/version.h>
#include <platform.h>
#include <printf.h>
#include <string.h>

#include <kernel/thread.h>

crashlog_t crashlog = {};

size_t crashlog_to_string(char* out, const size_t out_len, CrashlogType type) {
  char* buf = out;
  size_t remain = out_len;
  size_t len;
  const bool is_oom = type == CrashlogType::OOM;

  buf[0] = '\0';

  len = snprintf(buf, remain, "ZIRCON %s\n\n", is_oom ? "OOM" : "KERNEL PANIC");
  if (len > remain) {
    return out_len;
  }
  remain -= len;
  buf += len;

  len = snprintf(buf, remain, "UPTIME (ms)\n%" PRIi64 "\n\n", current_time() / ZX_MSEC(1));
  if (len > remain) {
    return out_len;
  }
  remain -= len;
  buf += len;

  // Keep the format and values in sync with the symbolizer.
  // Print before the registers (KASLR offset).
#if defined(__x86_64__)
  const char* arch = "x86_64";
#elif defined(__aarch64__)
  const char* arch = "aarch64";
#endif
  len = snprintf(buf, remain,
                 "VERSION\narch: %s\nbuild_id: %s\ndso: id=%s base=%#lx name=zircon.elf\n\n", arch,
                 version.buildid, version.elf_build_id, is_oom ? 0u : crashlog.base_address);
  if (len > remain) {
    return out_len;
  }
  remain -= len;
  buf += len;

  if (is_oom) {
    // If OOM, then including a backtrace doesn't make sense, return early.
    return out_len - remain;
  }

  if (crashlog.iframe) {
#if defined(__aarch64__)
    len = snprintf(buf, remain,
                   "REGISTERS\n"
                   "  x0: %#18" PRIx64
                   "\n"
                   "  x1: %#18" PRIx64
                   "\n"
                   "  x2: %#18" PRIx64
                   "\n"
                   "  x3: %#18" PRIx64
                   "\n"
                   "  x4: %#18" PRIx64
                   "\n"
                   "  x5: %#18" PRIx64
                   "\n"
                   "  x6: %#18" PRIx64
                   "\n"
                   "  x7: %#18" PRIx64
                   "\n"
                   "  x8: %#18" PRIx64
                   "\n"
                   "  x9: %#18" PRIx64
                   "\n"
                   " x10: %#18" PRIx64
                   "\n"
                   " x11: %#18" PRIx64
                   "\n"
                   " x12: %#18" PRIx64
                   "\n"
                   " x13: %#18" PRIx64
                   "\n"
                   " x14: %#18" PRIx64
                   "\n"
                   " x15: %#18" PRIx64
                   "\n"
                   " x16: %#18" PRIx64
                   "\n"
                   " x17: %#18" PRIx64
                   "\n"
                   " x18: %#18" PRIx64
                   "\n"
                   " x19: %#18" PRIx64
                   "\n"
                   " x20: %#18" PRIx64
                   "\n"
                   " x21: %#18" PRIx64
                   "\n"
                   " x22: %#18" PRIx64
                   "\n"
                   " x23: %#18" PRIx64
                   "\n"
                   " x24: %#18" PRIx64
                   "\n"
                   " x25: %#18" PRIx64
                   "\n"
                   " x26: %#18" PRIx64
                   "\n"
                   " x27: %#18" PRIx64
                   "\n"
                   " x28: %#18" PRIx64
                   "\n"
                   " x29: %#18" PRIx64
                   "\n"
                   "  lr: %#18" PRIx64
                   "\n"
                   " usp: %#18" PRIx64
                   "\n"
                   " elr: %#18" PRIx64
                   "\n"
                   "spsr: %#18" PRIx64
                   "\n"
                   "\n",
                   crashlog.iframe->r[0], crashlog.iframe->r[1], crashlog.iframe->r[2],
                   crashlog.iframe->r[3], crashlog.iframe->r[4], crashlog.iframe->r[5],
                   crashlog.iframe->r[6], crashlog.iframe->r[7], crashlog.iframe->r[8],
                   crashlog.iframe->r[9], crashlog.iframe->r[10], crashlog.iframe->r[11],
                   crashlog.iframe->r[12], crashlog.iframe->r[13], crashlog.iframe->r[14],
                   crashlog.iframe->r[15], crashlog.iframe->r[16], crashlog.iframe->r[17],
                   crashlog.iframe->r[18], crashlog.iframe->r[19], crashlog.iframe->r[20],
                   crashlog.iframe->r[21], crashlog.iframe->r[22], crashlog.iframe->r[23],
                   crashlog.iframe->r[24], crashlog.iframe->r[25], crashlog.iframe->r[26],
                   crashlog.iframe->r[27], crashlog.iframe->r[28], crashlog.iframe->r[29],
                   crashlog.iframe->lr, crashlog.iframe->usp, crashlog.iframe->elr,
                   crashlog.iframe->spsr);
    if (len > remain) {
      return out_len;
    }
    remain -= len;
    buf += len;
#elif defined(__x86_64__)
    len = snprintf(buf, remain,
                   "REGISTERS\n"
                   "  CS: %#18" PRIx64
                   "\n"
                   " RIP: %#18" PRIx64
                   "\n"
                   " EFL: %#18" PRIx64
                   "\n"
                   " CR2: %#18lx\n"
                   " RAX: %#18" PRIx64
                   "\n"
                   " RBX: %#18" PRIx64
                   "\n"
                   " RCX: %#18" PRIx64
                   "\n"
                   " RDX: %#18" PRIx64
                   "\n"
                   " RSI: %#18" PRIx64
                   "\n"
                   " RDI: %#18" PRIx64
                   "\n"
                   " RBP: %#18" PRIx64
                   "\n"
                   " RSP: %#18" PRIx64
                   "\n"
                   "  R8: %#18" PRIx64
                   "\n"
                   "  R9: %#18" PRIx64
                   "\n"
                   " R10: %#18" PRIx64
                   "\n"
                   " R11: %#18" PRIx64
                   "\n"
                   " R12: %#18" PRIx64
                   "\n"
                   " R13: %#18" PRIx64
                   "\n"
                   " R14: %#18" PRIx64
                   "\n"
                   " R15: %#18" PRIx64
                   "\n"
                   "errc: %#18" PRIx64
                   "\n"
                   "\n",
                   crashlog.iframe->cs, crashlog.iframe->ip, crashlog.iframe->flags, x86_get_cr2(),
                   crashlog.iframe->rax, crashlog.iframe->rbx, crashlog.iframe->rcx,
                   crashlog.iframe->rdx, crashlog.iframe->rsi, crashlog.iframe->rdi,
                   crashlog.iframe->rbp, crashlog.iframe->user_sp, crashlog.iframe->r8,
                   crashlog.iframe->r9, crashlog.iframe->r10, crashlog.iframe->r11,
                   crashlog.iframe->r12, crashlog.iframe->r13, crashlog.iframe->r14,
                   crashlog.iframe->r15, crashlog.iframe->err_code);
    if (len > remain) {
      return out_len;
    }
    remain -= len;
    buf += len;
#endif
  }

  len = snprintf(buf, remain, "BACKTRACE (up to 16 calls)\n");
  if (len > remain) {
    return out_len;
  }
  remain -= len;
  buf += len;

  len = thread_append_current_backtrace(buf, remain);
  if (len > remain) {
    return out_len;
  }
  remain -= len;
  buf += len;

  len = snprintf(buf, remain, "\n");
  if (len > remain) {
    return out_len;
  }
  remain -= len;
  buf += len;

  return out_len - remain;
}
